#include "lxz_config.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

#ifdef LXZ_CFG_OS_WIN32
  #define _WIN32_WINNT 0x400
  #include <direct.h>
  #include <io.h>
  #include <windows.h>
  #include <errno.h>
#endif /* LXZ_CFG_OS_WIN32 */

#ifdef LXZ_CFG_OS_LINUX
  #include <sys/types.h>
  #include <sys/socket.h>
  #include <netinet/in.h>
  #include <arpa/inet.h>
  #include <netdb.h>
  #include <sys/ioctl.h>
  #include <sys/poll.h>
  #include <time.h>
  #include <pthread.h>
  #include <fcntl.h>

  #include <unistd.h>
  #include <errno.h>
  #include <dirent.h>
#endif /* LXZ_CFG_OS_LINUX */

#include "lxz_types.h"
#include "lxz_string.h"
#include "lxz_runlog.h"
#include "lxz_debug.h"
#include "lxz_exec.h"
#include "os_port.h"

#ifdef LXZ_CFG_OS_WIN32
#pragma comment(lib, "ws2_32.lib")

void osp_timer_f_sleep(uint32 unMilliSeconds)
{
    Sleep(unMilliSeconds);
}

/*
 * initialize a socket
 *
 * @param: none
 *
 * return: sockfd, a socket description
 */
sint32 osp_socket_f_open(int af, int type, int protocol)
{
    SOCKET sockfd = -1;

    WORD wVerReq = 0;
    WSADATA wsaData;
    int errNo = 0;

    OS_DBG_INF(("os_socket_open, begin\n"));

    wVerReq = MAKEWORD(1,1);
    errNo = WSAStartup(wVerReq, &wsaData);

    if (errNo != 0)
    {
        OS_DBG_DBG(("there's no useable winsock.dll\n"));
        return 0;
    }

    /* Confirm that the Windows Sockets DLL supports 1.1. */
    if ((LOBYTE(wsaData.wVersion) != 1) || (HIBYTE(wsaData.wVersion) != 1))
    {
        WSACleanup(); 
        return 0; 
    }

    sockfd = socket(af, type, 0);
    if(sockfd < 0)
    {
        OS_DBG_DBG(("os_socket_open fail!\n"));
        return 0;
    }

    OS_DBG_INF(("os_socket_open, end\n"));
    return sockfd;
}

/*
 * bind socket with address-port
 *
 * @param: none
 *
 * return: sockfd, a socket description
 */
sint32 osp_socket_f_bind(sint32 sockfd, uint16 uport)
{
    struct sockaddr_in srvaddr;

    OS_DBG_INF(("osp_f_socket_bind, begin\n"));

    if(sockfd < 0)
    {
        OS_DBG_ERR(("osp_f_socket_bind, parameters error\n"));
        OS_DBG_INF(("osp_f_socket_bind, end\n"));
        return 0;
    }

    memset(&srvaddr,0x00,sizeof(struct sockaddr));
    srvaddr.sin_family = AF_INET;
    srvaddr.sin_port = htons(uport);
    srvaddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);

    if(bind(sockfd,(struct sockaddr *)&srvaddr,sizeof(struct sockaddr))<0)
    {
        OS_DBG_ERR(("osp_f_socket_bind, bind error\n"));
        OS_DBG_INF(("osp_f_socket_bind, end\n"));
        return 0;
    }

    OS_DBG_INF(("osp_f_socket_bind, end\n"));
    return 1;
}

/*
 * listen on a port
 *
 * @param: sockfd, a socket description
 * @param: ipaddr, the IP of remote host
 * @param: uport, the port of remote host 
 *
 * return: 1, success; 0, fail
 */
sint32 osp_socket_f_connect(sint32 sockfd, char * ipaddr, uint16 uport)
{
    struct sockaddr_in dstsrvr;

    OS_DBG_INF(("osp_socket_f_connect, begin\n"));

    memset(&dstsrvr, 0, sizeof(dstsrvr));
    dstsrvr.sin_family = AF_INET;
    dstsrvr.sin_addr.s_addr = inet_addr(ipaddr);
    dstsrvr.sin_port = htons(uport);

    connect(sockfd, (void *)&dstsrvr, sizeof(dstsrvr));

    OS_DBG_INF(("osp_socket_f_connect, end\n"));
    return sockfd;
}

/*
 * listen on a port
 *
 * @param: sockfd, a socket description
 * @param: backlog, length of waiting queue
 *
 * return: 1, success; 0, fail
 */
sint32 osp_socket_f_listen(sint32 sockfd, uint16 backlog)
{
    OS_DBG_INF(("os_socket_listen, begin\n"));

    if(sockfd < 0)
    {
        OS_DBG_ERR(("os_socket_listen, parameters error\n"));
        OS_DBG_INF(("os_socket_listen, end\n"));
        return 0;
    }

    if(listen(sockfd, backlog)<0)
    {
        OS_DBG_ERR(("os_socket_listen, listen error\n"));
        OS_DBG_INF(("os_socket_listen, end\n"));
        return 0;
    }

    OS_DBG_INF(("os_socket_listen, end\n"));
    return 1;
}

/*
 * waiting and receiving connect-request from remote host
 *
 * @param: lis_sockfd, a socket description
 *
 * return: apt_sockfd, a socket description
 */
sint32 osp_socket_f_accept(sint32 lis_sockfd)
{
    SOCKET apt_sockfd;
    struct sockaddr_in remote;
    int size = sizeof(struct sockaddr_in);
    OS_DBG_INF(("os_socket_accept, begin\n"));
    
    apt_sockfd = accept(lis_sockfd,(struct sockaddr *)&remote,&size);

    OS_DBG_INF(("os_socket_accept, end\n"));
    return apt_sockfd;
}

/*
 * receive data by socket
 *
 * @param: sockfd, a socket description
 * @param: up_buff, a buffer for saving data 
 * @param: buff_size, max length of data that can be received
 *
 * return: the length of data has been received
 */
sint32 osp_socket_f_read(sint32 sockfd, uint08 * up_buff, sint32 buff_size)
{
    sint32 nb_recv = 0;

    nb_recv = recv(sockfd, up_buff, buff_size, 0);

    return nb_recv;
}

/*
 * receive data by socket
 *
 * @param: sockfd, a socket description
 * @param: up_buff, a buffer for saving data 
 * @param: buff_size, max length of data that can be received
 *
 * return: the length of data has been received
 */
sint32 osp_socket_f_readfrom(sint32 sockfd, uint08 * up_buff, sint32 buff_size, 
                             void * addr_from, int * addr_len)
{
    sint32 nb_recv = 0;

    nb_recv = recvfrom(sockfd, up_buff, buff_size, 0, (struct sockaddr *)addr_from, (int *)addr_len);

    return nb_recv;
}

/*
 * send data by socket
 *
 * @param: sockfd, a socket description
 * @param: up_buff, point to the header of data 
 * @param: len, length of data that will be sent
 *
 * return: the length of data has been sent
 */
sint32 osp_socket_f_write(sint32 sockfd, uint08 * up_buff, sint32 len)
{
    sint32 n_bytes_sent_total = 0;

    sint32 n_bytes_sent = 0;
    sint32 n_bytes_left = len;
    uint08 * up_new_src = up_buff;
    

    while (n_bytes_left > 0)
    {
        n_bytes_sent = send(sockfd, up_new_src, n_bytes_left, 0);
        if (n_bytes_sent > 0)
        {
            n_bytes_sent_total += n_bytes_sent;

            up_new_src += n_bytes_sent;
            n_bytes_left -= n_bytes_sent;
        }
        else
        {
            break;
        }
    }

    return n_bytes_sent;
}

/*
 * send data by socket
 *
 * @param: sockfd, a socket description
 * @param: up_buff, point to the header of data 
 * @param: len, length of data that will be sent
 *
 * return: the length of data has been sent
 */
sint32 osp_socket_f_writeto(sint32 sockfd, uint08 * up_buff, sint32 len, 
                            void * addr_to, int addr_len)
{
    sint32 n_bytes_sent_total = 0;

    sint32 n_bytes_sent = 0;
    sint32 n_bytes_left = len;
    uint08 * up_new_src = up_buff;
    

    while (n_bytes_left > 0)
    {
        n_bytes_sent = sendto(sockfd, up_new_src, n_bytes_left, 0, addr_to, addr_len);

        if (n_bytes_sent > 0)
        {
            n_bytes_sent_total += n_bytes_sent;

            up_new_src += n_bytes_sent;
            n_bytes_left -= n_bytes_sent;
        }
        else
        {
            break;
        }
    }

    return n_bytes_sent;
}

/*
 * close connection
 *
 * @param: sockfd, a socket description
 *
 * return: none
 */
void osp_socket_f_close(sint32 sockfd)
{
    OS_DBG_INF(("osp_f_socket_close, begin\n"));
    OS_DBG_DBG(("osp_f_socket_close, sockfd = %d\n", sockfd));

    closesocket(sockfd);

    OS_DBG_INF(("osp_f_socket_close, end\n"));
}

uint32 osp_socket_f_getpeeraddr(sint32 sockfd)
{
    uint32 i_ip_addr = 0;

    sint32 i_op_status = 0;
    struct sockaddr_in* p_in_addr;
    struct sockaddr a_peer_addr;
    sint32 i_name_len = sizeof(struct sockaddr);

    i_op_status = getpeername(sockfd, &a_peer_addr, &i_name_len);
    p_in_addr = (struct sockaddr_in*)(&a_peer_addr);
    i_ip_addr =  inet_addr(inet_ntoa(p_in_addr->sin_addr));

    return i_ip_addr;
}

sint32 osp_socket_f_geterror(void)
{
    sint32 i_op_status = 0;

    i_op_status = WSAGetLastError();

    return i_op_status;
}

/*
 * open a file and return file description
 *
 * @param: u_rel_directory, relative directory
 * @param: u_file_name, file-name of the specified file
 *
 * return: fp_open, file description.
 */
FILE * osp_file_f_fopen(uint08 * p_rel_directory, uint08 * p_file_name, uint08 * p_open_mode)
{
    FILE * fp_open = NULL;
    uint08 llbuf[OSP_MAX_FULL_PATH];

    memset(llbuf, 0, sizeof(llbuf));
    sprintf(llbuf, "./%s/%s", p_rel_directory, p_file_name);
    fp_open = fopen(llbuf, p_open_mode);

    return fp_open;
}

sint32 osp_thread_f_open(osp_f_thread_t fp_thread_entry, void * pctxt, void * pthreadid)
{
    sint32 i_op_status = 0;

    HANDLE h_thread_handle;

    h_thread_handle = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)fp_thread_entry, pctxt, 0, pthreadid);
    if (INVALID_HANDLE_VALUE != h_thread_handle)
    {
        i_op_status = 1;
    }

    return i_op_status;
}

sint32 osp_thread_f_close(void *pthread)
{
    sint32 i_op_status = 0;

    i_op_status = CloseHandle(pthread);

    return i_op_status;
}
#endif /* LXZ_CFG_OS_WIN32 */

#ifdef LXZ_CFG_OS_LINUX
void osp_timer_f_sleep(uint32 unMilliSeconds)
{
    usleep(unMilliSeconds * 1000);
}

/*
 * initialize a socket
 *
 * @param: none
 *
 * return: sockfd, a socket description
 */
sint32 osp_socket_f_open(int af, int type, int protocol)
{
    sint32 sockfd = -1;

    int errNo = 0;

    OS_DBG_INF(("os_socket_open, begin\n"));

    sockfd = socket(af, type, 0);
    if(sockfd < 0)
    {
        OS_DBG_DBG(("os_socket_open fail!\n"));
        return 0;
    }

    OS_DBG_INF(("os_socket_open, end\n"));
    return sockfd;
}

/*
 * bind socket with address-port
 *
 * @param: none
 *
 * return: sockfd, a socket description
 */
sint32 osp_socket_f_bind(sint32 sockfd, uint16 uport)
{
    struct sockaddr_in srvaddr;

    OS_DBG_INF(("osp_f_socket_bind, begin\n"));

    if(sockfd < 0)
    {
        OS_DBG_ERR(("osp_f_socket_bind, parameters error\n"));
        OS_DBG_INF(("osp_f_socket_bind, end\n"));
        return 0;
    }

    memset(&srvaddr,0x00,sizeof(struct sockaddr));
    srvaddr.sin_family = AF_INET;
    srvaddr.sin_port = htons(uport);
    srvaddr.sin_addr.s_addr = INADDR_ANY;

    if(bind(sockfd,(struct sockaddr *)&srvaddr,sizeof(struct sockaddr))<0)
    {
        OS_DBG_ERR(("osp_f_socket_bind, bind error\n"));
        OS_DBG_INF(("osp_f_socket_bind, end\n"));
        return 0;
    }

    OS_DBG_INF(("osp_f_socket_bind, end\n"));
    return 1;
}

/*
 * listen on a port
 *
 * @param: sockfd, a socket description
 * @param: ipaddr, the IP of remote host
 * @param: uport, the port of remote host 
 *
 * return: 1, success; 0, fail
 */
sint32 osp_socket_f_connect(sint32 sockfd, char * ipaddr, uint16 uport)
{
    sint32 i_op_status = 0;

    struct sockaddr_in dstsrvr;
    struct timeval tv;

    OS_DBG_INF(("osp_socket_f_connect, begin\n"));

    if(sockfd < 0)
    {
        OS_DBG_ERR("os_socket_listen, parameters error\n");
        OS_DBG_INF("os_socket_listen, end\n");
        return 0;
    }

    memset(&dstsrvr, 0, sizeof(dstsrvr));
    dstsrvr.sin_family = AF_INET;
    dstsrvr.sin_addr.s_addr = inet_addr(ipaddr);
    dstsrvr.sin_port = htons(uport);

    connect(sockfd, (void *)&dstsrvr, sizeof(dstsrvr));

    tv.tv_sec = 1;
    tv.tv_usec = 0;
    if(setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))<0){
        OS_DBG_ERR("socket option  SO_RCVTIMEO not support\n");
        return i_op_status;
    }

    i_op_status = 1;
    OS_DBG_INF(("osp_socket_f_connect, end\n"));
    return i_op_status;
}

/*
 * listen on a port
 *
 * @param: sockfd, a socket description
 * @param: backlog, length of waiting queue
 *
 * return: 1, success; 0, fail
 */
sint32 osp_socket_f_listen(sint32 sockfd, uint16 backlog)
{
    OS_DBG_INF(("os_socket_listen, begin\n"));

    if(sockfd < 0)
    {
        OS_DBG_ERR(("os_socket_listen, parameters error\n"));
        OS_DBG_INF(("os_socket_listen, end\n"));
        return 0;
    }

    if(listen(sockfd, backlog)<0)
    {
        OS_DBG_ERR(("os_socket_listen, listen error\n"));
        OS_DBG_INF(("os_socket_listen, end\n"));
        return 0;
    }

    OS_DBG_INF(("os_socket_listen, end\n"));
    return 1;
}

/*
 * waiting and receiving connect-request from remote host
 *
 * @param: lis_sockfd, a socket descriptor
 *
 * return: apt_sockfd, a socket descriptor
 */
sint32 osp_socket_f_accept(sint32 lis_sockfd)
{
    sint32 apt_sockfd;

    struct sockaddr_in remote;
    int size = sizeof(struct sockaddr_in);
    OS_DBG_INF(("os_socket_accept, begin\n"));
    
    apt_sockfd = accept(lis_sockfd,(struct sockaddr *)&remote,&size);

    OS_DBG_INF(("os_socket_accept, end\n"));
    return apt_sockfd;
}

/*
 * receive data by socket
 *
 * @param: sockfd, a socket descriptor
 * @param: up_buff, a buffer for saving data 
 * @param: buff_size, max length of data that can be received
 *
 * return: the length of data has been received
 */
sint32 osp_socket_f_read(sint32 sockfd, uint08 * up_buff, sint32 buff_size)
{
    sint32 nb_recv = 0;

    nb_recv = recv(sockfd, up_buff, buff_size, 0);

    return nb_recv;
}

/*
 * receive data by socket
 *
 * @param: sockfd, a socket description
 * @param: up_buff, a buffer for saving data 
 * @param: buff_size, max length of data that can be received
 *
 * return: the length of data has been received
 */
sint32 osp_socket_f_readfrom(sint32 sockfd, uint08 * up_buff, sint32 buff_size, 
                             void * addr_from, int * addr_len)
{
    sint32 nb_recv = 0;

    nb_recv = recvfrom(sockfd, up_buff, buff_size, 0, (struct sockaddr *)addr_from, (int *)addr_len);

    return nb_recv;
}

/*
 * send data by socket
 *
 * @param: sockfd, a socket description
 * @param: up_buff, point to the header of data 
 * @param: len, length of data that will be sent
 *
 * return: the length of data has been sent
 */
sint32 osp_socket_f_write(sint32 sockfd, uint08 * up_buff, sint32 len)
{
    sint32 n_bytes_sent_total = 0;

    sint32 n_bytes_sent = 0;
    sint32 n_bytes_left = len;
    uint08 * up_new_src = up_buff;
    

    while (n_bytes_left > 0)
    {
        n_bytes_sent = send(sockfd, up_new_src, n_bytes_left, 0);
        if (n_bytes_sent > 0)
        {
            n_bytes_sent_total += n_bytes_sent;

            up_new_src += n_bytes_sent;
            n_bytes_left -= n_bytes_sent;
        }
        else
        {
            break;
        }
    }

    return n_bytes_sent;
}

/*
 * send data by socket
 *
 * @param: sockfd, a socket description
 * @param: up_buff, point to the header of data 
 * @param: len, length of data that will be sent
 *
 * return: the length of data has been sent
 */
sint32 osp_socket_f_writeto(sint32 sockfd, uint08 * up_buff, sint32 len, 
                            void * addr_to, int addr_len)
{
    sint32 n_bytes_sent_total = 0;

    sint32 n_bytes_sent = 0;
    sint32 n_bytes_left = len;
    uint08 * up_new_src = up_buff;
    

    while (n_bytes_left > 0)
    {
        n_bytes_sent = sendto(sockfd, up_new_src, n_bytes_left, 0, addr_to, addr_len);

        if (n_bytes_sent > 0)
        {
            n_bytes_sent_total += n_bytes_sent;

            up_new_src += n_bytes_sent;
            n_bytes_left -= n_bytes_sent;
        }
        else
        {
            break;
        }
    }

    return n_bytes_sent;
}

/*
 * close connection
 *
 * @param: sockfd, a socket description
 *
 * return: none
 */
void osp_socket_f_close(sint32 sockfd)
{
    OS_DBG_INF(("osp_f_socket_close, begin\n"));
    OS_DBG_DBG(("osp_f_socket_close, sockfd = %d\n", sockfd));

    close(sockfd);

    OS_DBG_INF(("osp_f_socket_close, end\n"));
}

uint32 osp_socket_f_getpeeraddr(sint32 sockfd)
{
    uint32 i_ip_addr = 0;

    sint32 i_op_status = 0;
    struct sockaddr_in* p_in_addr;
    struct sockaddr a_peer_addr;
    socklen_t addrlen = sizeof(struct sockaddr);

    i_op_status = getpeername(sockfd, &a_peer_addr, &addrlen);
    p_in_addr = (struct sockaddr_in*)(&a_peer_addr);
    i_ip_addr =  inet_addr(inet_ntoa(p_in_addr->sin_addr));

    return i_ip_addr;
}

sint32 osp_socket_f_geterror(void)
{
    sint32 i_op_status = 0;

    return i_op_status;
}

/*
 * open a file and return file description
 *
 * @param: u_rel_directory, relative directory
 * @param: u_file_name, file-name of the specified file
 *
 * return: fp_open, file description.
 */
FILE * osp_file_f_fopen(uint08 * p_rel_directory, uint08 * p_file_name, uint08 * p_open_mode)
{
    FILE * fp_open = NULL;
    uint08 llbuf[OSP_MAX_FULL_PATH];

    memset(llbuf, 0, sizeof(llbuf));
    sprintf(llbuf, "./%s/%s", p_rel_directory, p_file_name);
    fp_open = fopen(llbuf, p_open_mode);

    return fp_open;
}

sint32 osp_thread_f_open(osp_f_thread_t fp_thread_entry, void * pctxt, void * pthreadid)
{
    sint32 i_op_status = 0;
    sint32 h_cur_thread = 0;

    pthread_attr_t attr;

    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    i_op_status = pthread_create((pthread_t *)pthreadid, &attr, (void*)fp_thread_entry, pctxt);

    if (i_op_status == 0)
    {
        h_cur_thread = (sint32)pthreadid;
    }

    return h_cur_thread;
}

sint32 osp_thread_f_close(void *pthread)
{
    sint32 i_op_status = 0;

    i_op_status = pthread_cancel(*((pthread_t *)pthread));

    return i_op_status;
}
#endif /* LXZ_CFG_OS_LINUX */

